/*=============================================================================
#    The element-comm is a tentative program used to test our ideas.           
#                       Copyright (C)  bgao                                    
#
#     This program is free software; you can redistribute it and/or            
#      modify it under the terms of the GNU General Public License             
#     as published by the Free Software Foundation; either version 2           
#         of the License, or (at your option) any later version.               
#
#     This program is distributed in the hope that it will be useful,          
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            
#             GNU General Public License for more details.                    
#
#    You should have received a copy of the GNU General Public License        
#     along with this program; if not, write to the Free Software             
#      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,             
#                       MA  02110-1301, USA.                                  
#==============================================================================
# $Id$ 
#
# $Author$ 
#
# $Revision$ 
#
# $Date$ 
#
# Description: 
#
=============================================================================*/
#include "cc2420.h"
#include "SPI.h"

#include "rtos-wsn.h"

static void cc2420_random_init(void);

/* ------------------------------------------------------------------------------------------------
 *                                          Constants
 * ------------------------------------------------------------------------------------------------
 */
#define REG_READ      				0x40  /* binary: 01xx xxxx */
#define REG_WRITE					0x00  /* binary: 00xx xxxx */

#define RAM_ADDR_1stBYTE_MASK    	0x7F  /* binary: 0111 1111 */
#define RAM_ADDR_2ndBYTE_MASK   	0xC0  /* binary: 1100 0000 */
#define RAM_BIT        				0x80  /* binary: 1000 0000 */
#define RAM_READ_BIT      			0x20  /* binary: 0010 0000 */

#define FIFO_WRITE                  REG_WRITE
#define FIFO_READ                   REG_READ

/* ------------------------------------------------------------------------------------------------
 *                                          Low level function
 * ------------------------------------------------------------------------------------------------
 */




void cc2420_change_reg(uint8 addr,uint8 shift,uint16 mask,uint16 value)
{
	uint16 reg_old,reg_new;	
	wsn_EnterCritical();
	reg_old = cc2420_read_register(addr);
	reg_new = reg_old;
	reg_new &= ~mask;
	value = value << shift;
	reg_new |= (value & mask);
	if(reg_new != reg_old)
		cc2420_write_register(addr,reg_new);
	wsn_LeaveCritical();
}



uint8 cc2420_command(uint8 cmd)
{
	uint8 retval;
	wsn_EnterCritical();
	/* Enable CS */
	CC2420_CS_ENABLE();

	/* Send this command */
	SPI_SEND(cmd);

	/* Return the status */
	SPI_STATUS(retval);

	/* Disable CS */
	CC2420_CS_DISABLE();
	wsn_LeaveCritical();
	return retval;
}

uint16 cc2420_read_register(uint8 addr)
{
	uint16 retval;
	wsn_EnterCritical();
	/* Enable CS */
	CC2420_CS_ENABLE();

	/* Send register addrss */
	/*bit7(Reg/RAM) bit6(W/R)  bit5~bit0  */
	/*        0                  1               addr    */
	SPI_SEND((addr) | REG_READ);
	/* Send 0xFF to get byte ,the msb byte first. */
	SPI_RECEIVE_WORD(retval);
	/* Disable CS */
	CC2420_CS_DISABLE();	
	wsn_LeaveCritical();
	return retval;
}

void cc2420_write_register(uint8 addr, uint16 value)
{
	wsn_EnterCritical();
	/* Enable CS */
	CC2420_CS_ENABLE();

	/* Send register addrss */
	SPI_SEND(addr);
	/* Send data MSB byte first. */
	SPI_SEND_WORD(value);
	/* Disable CS */
	CC2420_CS_DISABLE();
	wsn_LeaveCritical();
}

void cc2420_read_ram(uint16 addr, uint8 len, uint8 *buf)
{
	uint8 * p_buf = buf;
	wsn_EnterCritical();
	/* Enable CS */
	CC2420_CS_ENABLE();

	/* Send address lsb first*/
	SPI_SEND((addr & RAM_ADDR_1stBYTE_MASK) | RAM_BIT);
	SPI_SEND(((addr >> 1) & RAM_ADDR_2ndBYTE_MASK) | RAM_READ_BIT);

	while (len -- ) {
		SPI_RECEIVE(*p_buf ++);
	}
	/* Disable CS */
	CC2420_CS_DISABLE();
	wsn_LeaveCritical();
}

void cc2420_write_ram(uint16 addr, uint8 len, uint8 *buf)
{
	uint8 * p_buf = buf;
	wsn_EnterCritical();
	//uint8 retval;
	/* Enable CS */
	CC2420_CS_ENABLE();

	/* Send RAM address  first byte*/
	SPI_SEND((addr & RAM_ADDR_1stBYTE_MASK) | RAM_BIT);
	/* Send RAM address  second byte*/
	SPI_SEND(((addr >> 1) & RAM_ADDR_2ndBYTE_MASK));

	while (len -- ) {
		SPI_SEND(*p_buf ++);
	}

	//retval = SPI_RX_REG;
	/* Disable CS */
	CC2420_CS_DISABLE();
	wsn_LeaveCritical();
}


uint8 cc2420_read_fifo(uint8 *buf, uint8 len)
{
	uint8 * p_buf = buf;
	wsn_EnterCritical();
	/* Enable CS */
	CC2420_CS_ENABLE();

	/* Send address lsb */
	SPI_SEND(CC2420_RXFIFO | FIFO_READ);

	while (len -- && CC2420_FIFO_IS_ACTIVE()) {
		SPI_RECEIVE(*p_buf ++);
	}
	/* Disable CS */
	CC2420_CS_DISABLE();
	wsn_LeaveCritical();
	return p_buf - buf;
}



void cc2420_write_fifo(uint8 *buf, uint8 len)
{
	uint8 *p_buf = buf;
	wsn_EnterCritical();
	//uint8 retval;
	/* Enable CS */
	CC2420_CS_ENABLE();	

	/* Send address  */
	SPI_SEND(CC2420_TXFIFO);

	while (len -- ) {		
		SPI_SEND(*p_buf ++);
	}

	/* Disable CS */
	CC2420_CS_DISABLE();
	wsn_LeaveCritical();
}

void cc2420_flush_Rxfifo(void)
{
	wsn_EnterCritical();
	/* Enable CS */
	CC2420_CS_ENABLE();
	/* Send address  */
	SPI_SEND(CC2420_RXFIFO | FIFO_READ);
	/* Read at least one byte */  
	SPI_SEND(0xFF);
	/* Disable CS */
	CC2420_CS_DISABLE();

	/* Send flush fifo command 2 times */
	cc2420_command(CC2420_SFLUSHRX);
	cc2420_command(CC2420_SFLUSHRX);
	wsn_LeaveCritical();
}

/* ------------------------------------------------------------------------------------------------
 *                                         high level function
 * ------------------------------------------------------------------------------------------------
 */

void cc2420_Oscillator_TurnOn(void)
{
	/* Enable cristal oscillator*/
	cc2420_command(CC2420_SXOSCON);
	/* Wait for crystal stable */
	while ( !(cc2420_status() & CC2420_MS_XOSC16M_STABLE) )
		;
}

void cc2420_reset(void)
{
	int i;
	/* Make a rising edge to reset */
	for ( i = 0; i < 1000; i ++ )
		CC2420_RESET_ACTIVE();	/* Set RESETn to 0 */
	for ( i = 0; i < 1000; i ++ )
		CC2420_RESET_INACTIVE();/* Set RESETn to 1 */

	/*init a random number*/
	cc2420_random_init();
	/*bug of cc2420,the reset value is not the recommend value*/
	cc2420_write_register(CC2420_REG_RXCTRL1, CC2420_RXCTRL1_INIT_VALUE);	
	/* Set I/O signal polarity */
	cc2420_write_register (CC2420_REG_IOCFG0, CC2420_IOC0_FIFOP_THR/* FIFOP_THR*/ | CC2420_IOC0_CCA_POLARITY /* CCA is active low */
			| CC2420_IOC0_SFD_POLARITY /* SFD is active low */ | CC2420_IOC0_FIFOP_POLARITY /* FIFOP is active low */
			| CC2420_IOC0_FIFOPOLARITY /* FIFO is active low */);

	/* Enable cristal oscillator */
	cc2420_Oscillator_TurnOn();
}


static uint16 random;
static void cc2420_random_init(void)
{
	/* put 16 random bits into the seed value */
	random = 0;
	{
		uint8 i;

		for(i=0; i<16; i++)
		{
			/* use most random bit of analog to digital receive conversion to populate the random seed */
			random = (random << 1) | (cc2420_read_register(CC2420_REG_ADCTST) & 0x01);
		}
	}

	/*The seed value must not be zero.  */	
	if (random == 0x0000)
	{
		random = 0xBEEF; /* completely arbitrary "random" value */
	}

}

#define RANDOM_POLY             0x1021
#define RANDOM_TOP_MOST_BIT     0x8000

uint8 cc2420_random_byte(void)
{
	uint8 i;

	for (i=0; i<8; i++)
	{
		if (random & RANDOM_TOP_MOST_BIT)
		{
			random = (random << 1) ^ RANDOM_POLY;
		}
		else
		{
			random = (random << 1);
		}
	}

	return (random & 0xFF);
}

/*****************************************************************                        
 *                                                 Encrypt data
 *****************************************************************/
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define MAX_DATALEN 16

static void cc2420_cipher16(uint8 *data, int len)
{

	len = MIN(len, MAX_DATALEN);

	cc2420_write_ram(CC2420_RAM_SABUF, len, data);
	cc2420_command(CC2420_SAES);
	cc2420_read_ram(CC2420_RAM_SABUF, len, data);
}

